package edu.tfh.s2.ehkbot.gerete.aktoren;

import edu.tfh.s2.ehkbot.daten.GeraeteRegistration;
import edu.tfh.s2.ehkbot.daten.Protokoll;
import edu.tfh.s2.ehkbot.steuerung.BusAdresse;
import edu.tfh.s2.ehkbot.steuerung.BusAdressierbar;
import edu.tfh.s2.ehkbot.util.Subject;

/**
 * MotorAktor bildet Motoren im System ab. Der MotorAktor nutzt das State Pattern um zwischen Simulation und
 * Real andere Verhalten zu Implementieren.
 * 
 * @author s2zehn
 */
public class MotorAktor extends Subject implements BusAdressierbar {

	/**
	 * Das State Interface für MotorAktorState. (Siehe State Pattern)
	 */
	public interface MotorAktorState {
		/**
		 * Dreht den Motor um x grad mit der Geschwindigkeit die in gradPerSec angegeben ist. drehe arbeitet
		 * Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
		 * 
		 * @see {@link MotorAktorState#waitUntilDone()}
		 * @param grad
		 *            Um wie viel Grad soll gedreht werden [rad].
		 * @param gradPerSec
		 *            Wie schnell soll gedreht werden [rad/s].
		 */
		public void drehe(double grad, double gradPerSec);

		/**
		 * Liefert die Akumuliere drehung des MotorAktors.
		 * 
		 * @return Die Drehung [rad]
		 */
		public double getDrehung();

		/**
		 * Liefert die aktuelle Geschwindigkeit.
		 * 
		 * @return Die Geschwindigkeit [rad/s].
		 */
		public double getGradPerSec();

		/**
		 * Liefer die maximale Geschwindigkeit.
		 * 
		 * @return Die maximale Geschwindigkeit [rad/s].
		 */
		public double getMaxSpeed();

		/**
		 * Liefert ob der MotorAktor sich in Zustand Aktiviert befindet.
		 * 
		 * @return true fals es sich in Zustand Aktivier befindet.
		 */
		public boolean istAktiviert();

		/**
		 * Stop den MotorAktor
		 */
		public void stope();

		/**
		 * Wartet bis die Drehung beendet wurde.
		 */
		public void waitUntilDone();
	}

	public static final double UMDREHUNG = 2 * Math.PI;
	private MotorAktorState aktivertStateInstance;
	private BusAdresse busAdresse;
	private MotorAktorState currentState;
	private MotorAktorState deaktivertStateInstance;

	/**
	 * Konstruktor für MotorAktor
	 * 
	 * @param busAdresse
	 *            Die BusAdresse.
	 * @param aktiviert
	 *            Das State für den Zustand Aktiviert.
	 * @param deaktiviert
	 *            Das State für den Zustand Deaktiviert.
	 */
	public MotorAktor(BusAdresse busAdresse, MotorAktorState aktiviert, MotorAktorState deaktiviert) {
		setBusAdresse(busAdresse);
		aktiviere();
		this.aktivertStateInstance = aktiviert;
		this.deaktivertStateInstance = deaktiviert;
		GeraeteRegistration.getInstance().register(this);
	}

	/**
	 * Aktivert den Zustand Aktiviert.
	 */
	public void aktiviere() {
		setState(aktivertStateInstance);
	}

	/**
	 * Aktiviert den Zustand Deaktivert.
	 */
	public void deaktiviere() {
		stope();
		setState(deaktivertStateInstance);
	}

	/**
	 * Dreht den Motor um x grad mit der Geschwindigkeit die von getMaxSpeed() geliefert wird. drehe arbeitet
	 * Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
	 * 
	 * @see {@link MotorAktorState#waitUntilDone()}
	 * @param grad
	 *            Um wie viel Grad soll gedreht werden [rad].
	 */
	public void drehe(double grad) {
		currentState.drehe(grad, currentState.getMaxSpeed());
	}

	/**
	 * Dreht den Motor um x grad mit der Geschwindigkeit die in gradPerSec angegeben ist. drehe arbeitet
	 * Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
	 * 
	 * @see {@link MotorAktorState#waitUntilDone()}
	 * @param grad
	 *            Um wie viel Grad soll gedreht werden [rad].
	 * @param gradPerSec
	 *            Wie schnell soll gedreht werden [rad/s].
	 */
	public void drehe(double grad, double gradPerSec) {
		currentState.drehe(grad, gradPerSec);
	}

	/**
	 * Dreht den Motor um x grad nach links mit der Geschwindigkeit die von getMaxSpeed() geliefert wird.
	 * drehe arbeitet Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
	 * 
	 * @see {@link MotorAktorState#waitUntilDone()}
	 * @param grad
	 *            Um wie viel Grad soll gedreht werden [rad].
	 */
	public void dreheLinks(double grad) {
		dreheLinks(grad, currentState.getMaxSpeed());
	}

	/**
	 * Dreht den Motor um x grad nach links mit der Geschwindigkeit die in gradPerSec angegeben ist. drehe
	 * arbeitet Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
	 * 
	 * @see {@link MotorAktorState#waitUntilDone()}
	 * @param grad
	 *            Um wie viel Grad soll gedreht werden [rad].
	 * @param gradPerSec
	 *            Wie schnell soll gedreht werden [rad/s].
	 */
	public void dreheLinks(double grad, double gradPerSec) {
		currentState.drehe(-grad, gradPerSec);
	}

	/**
	 * Dreht den Motor um x grad nach rechts mit der Geschwindigkeit die von getMaxSpeed() geliefert wird.
	 * drehe arbeitet Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
	 * 
	 * @see {@link MotorAktorState#waitUntilDone()}
	 * @param grad
	 *            Um wie viel Grad soll gedreht werden [rad].
	 */
	public void dreheRechts(double grad) {
		dreheRechts(grad, currentState.getMaxSpeed());
	}

	/**
	 * Dreht den Motor um x grad nach rechts mit der Geschwindigkeit die in gradPerSec angegeben ist. drehe
	 * arbeitet Asyncron d.h. die drehung ist nicht fertig wenn die Funktion beendet ist.
	 * 
	 * @see {@link MotorAktorState#waitUntilDone()}
	 * @param grad
	 *            Um wie viel Grad soll gedreht werden [rad].
	 * @param gradPerSec
	 *            Wie schnell soll gedreht werden [rad/s].
	 */
	public void dreheRechts(double grad, double gradPerSec) {
		currentState.drehe(grad, gradPerSec);
	}

	/**
	 * @see edu.tfh.s2.ehkbot.steuerung.BusAdressierbar#getBusAdresse()
	 */
	@Override
	public BusAdresse getBusAdresse() {
		return busAdresse;
	}

	/**
	 * Liefert die Akumuliere drehung des MotorAktors.
	 * 
	 * @return Die Drehung [rad]
	 */
	public double getDrehung() {
		return currentState.getDrehung();
	}

	/**
	 * Liefert die aktuelle Geschwindigkeit.
	 * 
	 * @return Die Geschwindigkeit [rad/s].
	 */
	public double getGradPerSec() {
		return currentState.getGradPerSec();
	}

	/**
	 * Liefert ob der MotorAktor sich in Zustand Aktiviert befindet.
	 * 
	 * @return true fals es sich in Zustand Aktivier befindet.
	 */
	public boolean istAktiviert() {
		return currentState.istAktiviert();
	}

	/**
	 * @see edu.tfh.s2.ehkbot.steuerung.BusAdressierbar#setBusAdresse(edu.tfh.s2.ehkbot.steuerung.BusAdresse)
	 */
	@Override
	public void setBusAdresse(BusAdresse busAdresse) {
		this.busAdresse = busAdresse;
	}

	/**
	 * Setzt den State (Siehe State Pattern)
	 * 
	 * @param state
	 */
	private void setState(MotorAktorState state) {
		if (currentState == state)
			Protokoll.getInstanc().warnung(
				"MotorAktor",
				"setState setzt ein state der schon gesetzt worden ist.",
				3);
		this.currentState = state;
	}

	/**
	 * Stop den MotorAktor
	 */
	public void stope() {
		currentState.stope();
	}

	/**
	 * Wartet bis die Drehung beendet wurde.
	 */
	public void waitUntilDone() {
		currentState.waitUntilDone();
	}
}